/*
 * LectorAritmetico.cpp
 *
 *  Created on: 06/04/2012
 *      Author: lucas
 */

#include "../hdr/LectorAritmetico.h"

#include <stdio.h>
#include <iostream>
#include <string.h>
#include "../hdr/FuncionesAritmeticas.h"

using namespace std;

unsigned int LectorAritmetico::tamanioBuffer = 1024;
unsigned int LectorAritmetico::precision = 16;

LectorAritmetico::LectorAritmetico(FILE* archivo) :
		archivo(archivo), piso(0), techo((1 << precision) - 1), limite(
				1 << (precision - 1)), underflow(0), posicion(0), contadorBuffer(
				0), contadorSubBuffer(0) {
	buffer = new unsigned char[tamanioBuffer];
	this->leerBufferDeArchivo();
	this->leerBitsDeBuffer(precision);
	archivoDebug = fopen("debug.dbg", "rb");
}

LectorAritmetico::~LectorAritmetico() {
	fclose(archivoDebug);
	delete[] buffer;
}

unsigned short LectorAritmetico::leerSimbolo(unsigned short* largos,
		unsigned short total, unsigned short cantidad) {
	unsigned int pisoDebug, techoDebug, leyo = 1;
	leyo &= fread(&pisoDebug, sizeof(unsigned int), 1, archivoDebug);
	leyo &= fread(&techoDebug, sizeof(unsigned int), 1, archivoDebug);
	unsigned int largo_actual = this->techo - this->piso + 1;
//	unsigned int posicionObjetivo = (total * (posicion - this->piso + 1))
//			/ largo_actual;
	unsigned int largo = 0;
	unsigned int piso = 0;
	unsigned short simbolo;
	unsigned int nuevoPiso = this->piso;
	for (simbolo = 0; simbolo < cantidad; simbolo++) {
		piso += largo;
		largo = largos[simbolo];
		this->techo = this->piso + (largo_actual * (piso + largo)) / total - 1;
		nuevoPiso = this->piso + (largo_actual * piso) / total;
		if (this->techo >= posicion) {
			//printf("%d - %d - %d\n", nuevoPiso, this->techo, posicion);
			break;
		}
	}
	this->techo = this->piso + (largo_actual * (piso + largo)) / total - 1;
	this->piso += (largo_actual * piso) / total;
	this->renormalizar();
//	cout << (char) (simbolo) << " " << (void*) posicion << endl;
	return simbolo;
//	cout << contadorSubBuffer << "Piso:\t\t";
//	 this->imprimirNumero(this->piso);
//	 cout << "Techo:\t\t";
//	 this->imprimirNumero(this->techo);
//	this->renormalizar();
}

void LectorAritmetico::renormalizar() {
	unsigned int bitsEmitidos = 0;
	unsigned int emision = 0;
	unsigned int ultimoUnderflow = underflow;
	// Bits en común
	bitsEmitidos = FuncionesAritmeticas::emitirBits(piso, techo, precision,
			underflow, emision);
//	cout << ultimoUnderflow << " " << bitsEmitidos << " ";
	if (bitsEmitidos > 0) {
		FuncionesAritmeticas::sacarPrimerosBits(posicion, precision, 0,
				bitsEmitidos - ultimoUnderflow);
		this->leerBitsDeBuffer(bitsEmitidos - ultimoUnderflow);
	}
	// Manejar underflow
	ultimoUnderflow = FuncionesAritmeticas::actualizarUnderflow(piso, techo,
			precision);
	underflow += ultimoUnderflow;
	if (ultimoUnderflow) {
		int primerBit = posicion >= limite;
		FuncionesAritmeticas::sacarPrimerosBits(posicion, precision, 0,
				ultimoUnderflow + 1);
		posicion >>= 1;
		posicion |= primerBit ? limite : 0;
		this->leerBitsDeBuffer(ultimoUnderflow);
	}
}

void LectorAritmetico::leerBitsDeBuffer(unsigned int cantidadDeBits) {
	unsigned int bitsAInsertar = 0;
	unsigned char caracterActual = buffer[contadorBuffer];
	caracterActual <<= contadorSubBuffer;
	while (cantidadDeBits > 0) {
		cantidadDeBits--;
		bitsAInsertar <<= 1;
		if ((caracterActual & 0x80) != 0)
			bitsAInsertar |= 1;
		caracterActual <<= 1;
		contadorSubBuffer++;
		if (contadorSubBuffer >= 8) {
			contadorBuffer++;
			if (contadorBuffer >= tamanioBuffer)
				this->leerBufferDeArchivo();
			caracterActual = buffer[contadorBuffer];
//			printf("%d\n", contadorBuffer);
			contadorSubBuffer = 0;
		}
	}
	posicion |= bitsAInsertar;
	//cout << (void*)(posicion << (32 - 8*contadorBuffer - contadorSubBuffer)) << endl;
}

void LectorAritmetico::leerBufferDeArchivo() {
	memset(buffer, 0, tamanioBuffer * sizeof(char));
	fread(buffer, sizeof(char), tamanioBuffer, archivo);
	contadorBuffer = 0;
}


